#include "sdk.h"
#include "compilererrors.h"
#include <wx/arrimpl.cpp>

WX_DEFINE_OBJARRAY( ErrorsArray );

CompilerErrors::CompilerErrors()
  : m_ErrorIndex( -1 ) {
}

CompilerErrors::~CompilerErrors() {
}

void CompilerErrors::AddError( CompilerLineType lt, M_Code_Project* project, const wxString& filename, long int line, const wxString& error ) {
  CompileError err;
  err.lineType = lt;
  err.project = project;
  err.filename = filename;
  err.line = line;
  err.errors.Add( error );
  DoAddError( err );
}

void CompilerErrors::GotoError( int nr ) {
  if( m_Errors.GetCount() == 0 || nr < 0 || nr > ( int )m_Errors.GetCount() - 1 ) {
    return;
  }
  m_ErrorIndex = nr;
  DoGotoError( m_Errors[m_ErrorIndex] );
}

void CompilerErrors::Next() {
  if( m_ErrorIndex >= ( int )m_Errors.GetCount() - 1 ) {
    return;
  }
  int bkp = ++m_ErrorIndex;
  while( bkp < ( int )m_Errors.GetCount() ) {
    if( m_Errors[bkp].lineType == cltError ) {
      bool isNote =
        ( ( m_Errors[bkp].errors.GetCount() > 0 ) && m_Errors[bkp].errors[0].StartsWith( _T( "note:" ) ) );
      if( !isNote ) {
        m_ErrorIndex = bkp;
        break;
      }
    }
    ++bkp;
  }
  DoGotoError( m_Errors[m_ErrorIndex] );
}

void CompilerErrors::Previous() {
  if( m_ErrorIndex <= 0 ) {
    return;
  }
  int bkp = --m_ErrorIndex;
  while( bkp >= 0 ) {
    if( m_Errors[bkp].lineType == cltError ) {
      bool isNote =
        ( ( m_Errors[bkp].errors.GetCount() > 0 ) && m_Errors[bkp].errors[0].StartsWith( _T( "note:" ) ) );
      if( !isNote ) {
        m_ErrorIndex = bkp;
        break;
      }
    }
    --bkp;
  }
  DoGotoError( m_Errors[m_ErrorIndex] );
}

void CompilerErrors::Clear() {
  DoClearErrorMarkFromAllEditors();
  m_Errors.Clear();
  m_ErrorIndex = -1;
}

void CompilerErrors::DoAddError( const CompileError& error ) {
  m_Errors.Add( error );
}

int CompilerErrors::ErrorLineHasMore( const wxString& filename, long int line ) const {
  for( unsigned int i = 0; i < m_Errors.GetCount(); ++i ) {
    if( m_Errors[i].filename.Matches( filename ) &&
        m_Errors[i].line == line ) {
      return i;
    }
  }
  return -1;
}

void CompilerErrors::DoGotoError( const CompileError& error ) {
  if( error.line <= 0 ) {
    return;
  }
  DoClearErrorMarkFromAllEditors();
  M_Code_Editor* ed = 0;
  M_Code_Project* project = error.project ? error.project : Manager::Get()->GetProjectManager()->GetActiveProject();
  if( project && Manager::Get()->GetProjectManager()->IsProjectStillOpen( project ) ) {
    wxString filename = error.filename;
    bool isAbsolute = ( filename.Length() > 1 && filename.GetChar( 1 ) == ':' ) ||
                      filename.StartsWith( _T( "/" ) ) ||
                      filename.StartsWith( _T( "\\" ) );
    ProjectFile* f = project->GetFileByFilename( error.filename, !isAbsolute, true );
    if( f ) {
      ed = Manager::Get()->GetEditorManager()->Open( f->file.GetFullPath() );
      if( ed ) {
        ed->SetProjectFile( f );
      }
    } else {
      if( !isAbsolute ) {
        filename.Prepend( project->GetCommonTopLevelPath() );
      }
      ed = Manager::Get()->GetEditorManager()->Open( filename );
    }
  }
  if( !ed ) {
    ed = Manager::Get()->GetEditorManager()->Open( error.filename );
  }
  if( !ed ) {
    for( int i = 0; i < Manager::Get()->GetEditorManager()->GetEditorsCount(); ++i ) {
      M_Code_Editor* edit = Manager::Get()->GetEditorManager()->GetBuiltinEditor( i );
      if( !edit ) {
        continue;
      }
      ProjectFile* pf = edit->GetProjectFile();
      if( !pf ) {
        continue;
      }
      if( IsSuffixOfPath( error.filename, pf->file.GetFullPath() ) ) {
        ed = Manager::Get()->GetEditorManager()->Open( pf->file.GetFullPath() );
        break;
      }
    }
  }
  if( !ed && project ) {
    for( FilesList::iterator it = project->GetFilesList().begin(); it != project->GetFilesList().end(); ++it ) {
      ProjectFile* pf = *it;
      if( !pf ) {
        continue;
      }
      if( IsSuffixOfPath( error.filename, pf->file.GetFullPath() ) ) {
        ed = Manager::Get()->GetEditorManager()->Open( pf->file.GetFullPath() );
        break;
      }
    }
  }
  if( ed ) {
    ed->Activate();
    ed->UnfoldBlockFromLine( error.line - 1 );
    ed->GotoLine( error.line - 1 );
    ed->SetErrorLine( error.line - 1 );
  }
}

void CompilerErrors::DoClearErrorMarkFromAllEditors() {
  EditorManager* edMan = Manager::Get()->GetEditorManager();
  for( int i = 0; i < edMan->GetEditorsCount(); ++i ) {
    M_Code_Editor* ed = edMan->GetBuiltinEditor( i );
    if( ed ) {
      ed->SetErrorLine( -1 );
    }
  }
}

bool CompilerErrors::HasNextError() const {
  return m_ErrorIndex < ( int )m_Errors.GetCount();
}

bool CompilerErrors::HasPreviousError() const {
  return m_ErrorIndex > 0;
}

wxString CompilerErrors::GetErrorString( int index ) {
  if( m_Errors.GetCount() == 0 || index < 0 || index > ( int )m_Errors.GetCount() - 1 ) {
    return wxEmptyString;
  }
  wxArrayString& errors = m_Errors[index].errors;
  wxString error;
  if( errors.GetCount() ) {
    error = errors[0];
  }
  return error;
}

int CompilerErrors::GetFirstError() const {
  for( unsigned int i = 0; i < m_Errors.GetCount(); ++i ) {
    if( m_Errors[i].lineType == cltError ) {
      return i;
    }
  }
  return -1;
}

unsigned int CompilerErrors::GetCount( CompilerLineType lt ) const {
  unsigned int count = 0;
  for( unsigned int i = 0; i < m_Errors.GetCount(); ++i ) {
    if( m_Errors[i].lineType == lt ) {
      ++count;
    }
  }
  return count;
}
